/** 
 * cocos动态加载多语言图片资源 
 * 需求：不需要考虑性能与内存问题，最简化策略
 */

import DebugExt from "../base/DebugExt";

export default class LangImg {

    public static getIns(): LangImg {
        if (!LangImg._ins) {
            LangImg._ins = new LangImg();
        }
        return LangImg._ins;
    }
    private static _ins: LangImg;
    private constructor() {
        yy.event.on(null, yy.event.lang.LanuageChanged, this.onLanuageChanged, this);
    }
    private onDestory() {
        yy.event.off(yy.event.lang.LanuageChanged, this.onLanuageChanged, this);
    }
    public static destory() {
        if (LangImg._ins) {
            LangImg._ins.onDestory();
            delete LangImg._ins;
            LangImg._ins = null;
        }
    }

    public init(langList: string[] = [], baseLang: string = '') {
        this.uiLangList = langList;
        DebugExt.log("【LangImg】设置资源主语言：", baseLang);
        this._baseLang = baseLang;
    }
    private _baseLang: string = "en";

    // UI预期的多语言列表
    private _uiLangList: string[] = [];
    private set uiLangList(list: string[]) {
        if (list && list.length > 0) {
            this._uiLangList.splice(0);
            list.forEach(str => {
                if (str && str.length > 0)
                    this._uiLangList.push(str);
            });
        }
    }

    /** current language */
    private get curLan(): string {
        return yy.curLanguage;
    }

    /** base language */
    private get baseLan(): string {
        return this._baseLang;
    }

    /** 
     * preload language resources
     * @param langs 指定预加载的语言列表
     * @param subgames 子游戏多语言嵌套目录名称列表
     */
    public preload(langs?: string[], subgames?: string[]) {
        if (!Array.isArray(langs))
            langs = [];

        if (langs.length == 0) {
            const curLan = this.curLan;
            if (curLan != this.baseLan)
                langs.push(curLan);
            // base language
            if (this.baseLan && this.baseLan.length > 0)
                langs.push(this.baseLan);
        }

        if (Array.isArray(subgames) && subgames.length > 0) {
            this.subgames = subgames.filter(str => str && str.length > 0);
        }

        DebugExt.log("【LangImg】预加载多语言图片资源：", langs);
        langs.forEach((key: string) => this.loadLanguageRes(key, (suc: boolean, lan: string) => {
            if (suc && lan == this.curLan) {
                // 逻辑优化，当前语言加载完毕时，给出通知消息
                const e = new yy.event.lang.LanuageChanged;
                yy.event.emit(yy.event.lang.LanuageChanged, e);
            }
        }));
    }
    private subgames: string[] = [];

    /**
     * 获取当前语言的图片资源
     * 若不命中，则按照既定规则寻找可替代资源
     * @param name 图片资源名
     */
    public getIcon(name: string): cc.SpriteFrame {
        if (!name) {
            DebugExt.debug("【LangImg】getIcon资源名为空！");
            return null;
        }

        const curLan = this.curLan;
        const img = this.getLanguageIcon(name, curLan) || this.getFallbackIcon(name, curLan);
        return img || (curLan !== this.baseLan ? this.getLanguageIcon(name, this.baseLan) : null);
    }
    // 简繁体图片资源支持转换处理
    private getFallbackIcon(name: string, curLan: string): cc.SpriteFrame {
        if (curLan === "zh" && this.hasLanguage("zhf")) {
            return this.getLanguageIcon(name, "zhf");
        }
        if (curLan === "zhf" && this.hasLanguage("zh")) {
            return this.getLanguageIcon(name, "zh");
        }
        return null;
    }

    /** language file path */
    public get filePath(): string {
        return this._filePath;
    }
    public set filePath(path: string) {
        if (this._filePath != path && !path) {
            this._filePath = path;
            DebugExt.log("【LangImg】更新多语言资源相对目录为：./assets/resources/", path);
        }
    }
    private _filePath: string = "language";

    // resource map
    private _languageIconList: Map<string, Map<string, cc.SpriteFrame>> = new Map();
    // has language resource
    private hasLanguage(lan: string): boolean {
        return this._languageIconList.has(lan);
    }

    private loadLanguageRes(lan: string, completeCallback?: (loaded: boolean, lan: string) => void) {
        const path = this.filePath + '/' + lan;
        // check
        if (this.hasLanguage(lan)) {
            DebugExt.debug("【LangImg】重复加载资源目录:", path);
            if (completeCallback)
                completeCallback(true, lan);
            return;
        }
        const hasSubPath = this.subgames.length > 0;
        // load res
        this.loadResDir(path, lan, completeCallback);
        // load subgame res
        if (hasSubPath) {
            this.subgames.forEach(str => {
                const subPath = path + '/' + str;
                this.loadResDir(subPath, lan, completeCallback);
            });
        }
    }
    private loadResDir(path: string, lan: string, completeCallback: (loaded: boolean, lan: string) => void) {
        cc.loader.loadResDir(path, cc.SpriteFrame, (error: Error, resource: cc.SpriteFrame[], urls: string[]) => {
            const resMap: Map<string, cc.SpriteFrame> = new Map();
            this._languageIconList.set(lan, resMap);

            if (error || resource.length == 0) {
                DebugExt.debug("【LangImg】加载资源目录:", path, "出错！", error ? error.message : "空文件夹！");
                if (completeCallback)
                    completeCallback(false, lan);
            }
            else {
                resource.forEach((icon: cc.SpriteFrame) => {
                    if (icon && icon.isValid)
                        resMap.set(icon.name, icon);
                });
                DebugExt.debug("【LangImg】加载资源目录:", path, "完成！");
                DebugExt.table(resMap);
                if (completeCallback)
                    completeCallback(true, lan);
            }
        });
    }

    private getLanguageIcon(name: string, lan: string): cc.SpriteFrame {
        if (!lan || lan.length == 0
            || !this.hasLanguage(lan)
            || !name || name.length == 0)
            return null;
        const map = this._languageIconList.get(lan);
        if (!map || map.size == 0)
            return null;
        return map.get(name);
    }

    private onLanuageChanged(e: yy.event.lang.LanuageChanged) {
        const curLan = this.curLan;
        if (!this.hasLanguage(curLan))
            this.loadLanguageRes(curLan);
    }

}
